//
// Created by chao.li on 2023/10/20.
//

#ifndef SRC_LSYSTEM_LPARSER_H
#define SRC_LSYSTEM_LPARSER_H

#include <cstring>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <list>
#include <stack>
#include "../drawer_basic.h"

namespace l_sys
{
    class l_program
    {
        static bool is_white_space(const std::string& word);
        const static int MAX_LINE_CHAR_NUM = 100;
        static bool get_word(std::stringstream& ss, std::string& word);
    public:
        ///
        ///  Koch1 { ；程序名
        ///  Angle 6 ；规定角度增量为 360/6=60°
        ///  Axiom F--F--F ；通过公理给出初始图形：一个倒置的正三角形
        ///  F=F+F--F+F ；通过产生式给出代换规则；
        ///  }
        void parse(const std::string& programStr);
        std::string name;
        std::string axiom;
        std::vector<std::string> rules;
        int angle;
    };

    class l_action;
    class l_rule;
    class l_express
    {
    public:
        std::vector<l_action> actList;
        explicit l_express(const std::string& str);
        l_express(const l_express& str);
        l_express& operator = (const l_express& str);
        l_express(){}
        void apply(const l_rule& rule);
    };

    struct l_state
    {
        vec2 pos;
        int degree;
        color3 paint;
        int min_step;

        bool flip = false;
        void add_step()
        {
            auto delta = drawer_basic::get_direction(degree * drawer_basic::PI / 180) * min_step;
            pos = pos + delta;
        }

        void add_degree(int angle)
        {
            degree += angle;
        }

//        void set_color(const color3& col)
//        {
//            paint = col;
//        }
//        void set_clip()
//        {
//            flip = !flip;
//        }

    };
    class l_runner;
    class l_mac;
    struct l_action
    {
        char ch;
    public:
        explicit l_action(char cha): ch(cha){}
        l_action(): ch(' '){}
        l_action(const l_action& a) :ch(a.ch){}
        void execute(l_mac& machine);
    };

    class l_rule
    {
    public:
        l_action first;
        std::list<l_action> second;
        explicit l_rule(const std::string& str);
        l_rule() : first(){};
    };


    class l_runner
    {
        l_express cur_exp;
        std::vector<l_rule> rules;
        void translate(const l_program& prog)
        {
            rules.clear();
            for (int i = 0; i < prog.rules.size(); i++) {
                rules.emplace_back(prog.rules[i]);
            }
        }
    public:
        const int degree;
        l_express result;
        l_runner(const l_program& prog):
            cur_exp(prog.axiom),
            degree(360 / prog.angle)
        {
            translate(prog);
        }

        void execute(int steps)
        {
            result = cur_exp;
            for (int i = 0; i < steps; ++i) {
                for (int j = 0; j < rules.size(); ++j) {
                    result.apply(rules[j]);
                }
            }
        }
    };

    class l_mac : public drawer_basic
    {
        l_program source;
        std::shared_ptr<l_runner> runner;
    public:
        float get_degree()
        {
            return runner->degree;
        }

        l_mac():runner(nullptr)
        {
        }

        void process(const std::string& path)
        {
            std::ifstream ifs;
            ifs.open(path.c_str(), std::ios::in);
            std::string line;
            std::stringbuf text;
            while(!std::getline(ifs, line).eof()){
                text.sputn(line.c_str(), line.size());
                text.sputn("\r\n", 2);
            }
            source.parse(text.str());
        }

        void execute(int steps)
        {
            runner = std::make_shared<l_runner>(source);
            runner->execute(steps);
        }

        void draw(const vec2& start, int min_step, int degree = 0)
        {
            init_state(start, paint, min_step, degree);
            for (int i = 0; i < runner->result.actList.size(); ++i)
            {
                auto act = runner->result.actList[i];
                act.execute(*this);
//                std::cout << "i: " << i << ", angle: " << cur_state.degree / PI * 180;
            }
        }

        //---------draw state----------//
    protected:
    public:
        std::stack<l_state> stack;
        l_state cur_state;
        void init_state(const vec2& start, const color3& clr, int step, int degree)
        {
            cur_state.pos = start;
            cur_state.paint = clr;
            cur_state.min_step = step;
            cur_state.degree = degree;
        }

        void push_stack()
        {
            stack.push(cur_state);
        }

        void pop_stack()
        {
            cur_state = stack.top();
            stack.pop();
        }
    };
}

#endif //SRC_LSYSTEM_LPARSER_H
